#include "StdAfx.h"
#include "AudioDirectSound.h"
#include "Window.h"
#include <stdio.h>
#include "dx\SoundFXdx.h"
#include "dx\StreamingSounddx.h"
#include "core/core.h"

namespace directx {

AudioDirectSound::AudioDirectSound(void)
{
	lpds = NULL;
	lpListener = NULL;
}

AudioDirectSound::~AudioDirectSound(void)
{
	destroy();
}

audio::ISoundEffect* AudioDirectSound::createISoundEffect(const std::string &fileName, int numberOfVoices){
	HRESULT hr=0;
	
	audio::ISoundEffect* tempEffect = NULL;

	HMMIO m_hmmio;	
	LPSTR strFileName = (char*) fileName.c_str();

	m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );

	if(!m_hmmio)
	{
		coreGetLog()->WriteError("mmioOpen failed! Can't open: " + fileName, __FILE__, __LINE__);
		return false;
	}

	///////////////////////////////////////////////////////////////////////
	// Get the WAVEFORMATEX of the File
	///////////////////////////////////////////////////////////////////////
	WAVEFORMATEX* m_pwfx = new WAVEFORMATEX;
	
	MMCKINFO	mmWave;
	MMCKINFO	mmFmt;
	MMRESULT	mResult;

	ZeroMemory(&mmWave, sizeof(mmWave));
	ZeroMemory(&mmFmt, sizeof(mmFmt));

	// seek to the beginning of the file
	mResult = mmioSeek(m_hmmio, 0, SEEK_SET);
	
	// find the WAVE chunk
	mmWave.fccType = mmioStringToFOURCC("WAVE", 0);
	mmFmt.ckid = mmioStringToFOURCC("fmt ", 0);
	mResult = mmioDescend(m_hmmio, &mmWave, NULL, MMIO_FINDRIFF);
	if(mResult != MMSYSERR_NOERROR)
	{
		
		coreGetLog()->WriteError("mmioDescend failed. ", __FILE__, __LINE__);
		return false;
	}

	// find the fmt chunk
	mResult = mmioDescend(m_hmmio, &mmFmt, &mmWave, MMIO_FINDCHUNK);
	if(mResult != MMSYSERR_NOERROR)
	{
		coreGetLog()->WriteError("mmioDescend failed. ", __FILE__, __LINE__);
		return FALSE;
	}
	
	mResult = mmioRead(m_hmmio, (HPSTR)m_pwfx, sizeof(WAVEFORMATEX));
	if(mResult == -1)
	{
		coreGetLog()->WriteError("mmioRead failed. Can't get WAVEFORMATEX", __FILE__, __LINE__);
		return FALSE;
	}
	
	/// Take Out?
	//mmioAscend(m_hmmio, &mmFmt, 0);
	


	///////////////////////////////////////////////////////////////////////
	// Get the Size of the WaveData in Bytes (dwDSBufferSize)
	///////////////////////////////////////////////////////////////////////
	DWORD dwDSBufferSize = 0;

	MMCKINFO mmData;
	
	ZeroMemory(&mmWave, sizeof(mmWave));
	ZeroMemory(&mmData, sizeof(mmData));
	mResult = 0;
	
	mResult = mmioSeek(m_hmmio, 0, SEEK_SET);

	// find the WAVE chunk
	mmWave.fccType = mmioStringToFOURCC("WAVE", 0);
	mmData.ckid = mmioStringToFOURCC("data", 0);
	mResult = mmioDescend(m_hmmio, &mmWave, NULL, MMIO_FINDRIFF);
	if(mResult != MMSYSERR_NOERROR)
	{
		coreGetLog()->WriteError("mmioDescend failed. ", __FILE__, __LINE__);
		return FALSE;
	}

	// find the data chunk
	mResult = mmioDescend(m_hmmio, &mmData, &mmWave, MMIO_FINDCHUNK);
	if(mResult != MMSYSERR_NOERROR)
	{
		coreGetLog()->WriteError("mmioDescend failed. ", __FILE__, __LINE__);
		return FALSE;
	}

	dwDSBufferSize = mmData.cksize;

    
	
	///////////////////////////////////////////////////////////////////////
	// Create a static buffer with the size of the Data
	///////////////////////////////////////////////////////////////////////
	DSBUFFERDESC dsbd;
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
    dsbd.dwSize = sizeof(DSBUFFERDESC);
    dsbd.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY
				   | DSBCAPS_GLOBALFOCUS;
    dsbd.dwBufferBytes   = dwDSBufferSize;
    dsbd.guid3DAlgorithm = GUID_NULL;
    dsbd.lpwfxFormat     = m_pwfx;
	
	LPDIRECTSOUNDBUFFER tempBuffer=NULL;

	// DirectSound is only guarenteed to play PCM data.  Other
    // formats may or may not work depending the sound card driver.
    hr = lpds->CreateSoundBuffer( &dsbd, &tempBuffer, NULL );

    // Be sure to return this error code if it occurs so the
    // callers knows this happened.
	if( hr == DS_NO_VIRTUALIZATION ){
        coreGetLog()->WriteError("DS_NO_VIRTUALIZATION returned from 'CreateSoundBuffer'. ", __FILE__, __LINE__);
		//hrRet = DS_NO_VIRTUALIZATION;
	}
	
    LPDIRECTSOUNDBUFFER8 ppDsb8 = NULL;
	if (SUCCEEDED(hr)) 
	{ 
		hr = tempBuffer->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &ppDsb8);
		tempBuffer->Release();
	}
	

	///////////////////////////////////////////////////////////////////////
	// Fill Buffer with the WaveData
	///////////////////////////////////////////////////////////////////////
	
	ZeroMemory(&mmWave, sizeof(mmWave));
	ZeroMemory(&mmData, sizeof(mmData));
	mResult = 0;

	// seek to the beginning of the file
	mmioSeek(m_hmmio, 0, SEEK_SET);

	// find the WAVE chunk
	mmWave.fccType = mmioStringToFOURCC("WAVE", 0);
	mmData.ckid = mmioStringToFOURCC("data", 0);
	mResult = mmioDescend(m_hmmio, &mmWave, NULL, MMIO_FINDRIFF);
	if(mResult != MMSYSERR_NOERROR)
	{
		coreGetLog()->WriteError("mmioDescend failed. ", __FILE__, __LINE__);
		return FALSE;
	}

	// find the data chunk
	mResult = mmioDescend(m_hmmio, &mmData, &mmWave, MMIO_FINDCHUNK);
	if(mResult != MMSYSERR_NOERROR)
	{
		coreGetLog()->WriteError("mmioDescend failed. ", __FILE__, __LINE__);
		return FALSE;
	}

	VOID*   pDSLockedBuffer      = NULL; // Pointer to locked buffer memory
    DWORD   dwDSLockedBufferSize = 0;    // Size of the locked DirectSound buffer	
	
	// Lock the buffer down
	if( FAILED( hr = ppDsb8->Lock( 0, dwDSBufferSize,
                                 &pDSLockedBuffer, &dwDSLockedBufferSize,
								 NULL, NULL, 0L ) ) ){
		coreGetLog()->WriteError("Can't look Buffer. ", __FILE__, __LINE__);
		return false;
	}

	mResult = mmioRead(m_hmmio, (HPSTR)pDSLockedBuffer, dwDSLockedBufferSize);

	
	if(mResult == -1)
	{
		coreGetLog()->WriteError("Error reading wavedata. ", __FILE__, __LINE__);
		return false;
	}
	
	// Unlock the buffer, we don't need it anymore.
    ppDsb8->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );

	mmioClose(m_hmmio,0); 

	/// Use real Constructor
	tempEffect = new SoundFXdx(ppDsb8, lpds, numberOfVoices);
	return tempEffect;
}

audio::IStreamingSound* AudioDirectSound::createIStreamingSound(const std::string &fileName)
{
	audio::IStreamingSound* tempStream;	
	
	///////////////////////////////////////////////////////////////////////
	// Create the StreamingBuffer
	///////////////////////////////////////////////////////////////////////
	WAVEFORMATEX wfx; 
	DSBUFFERDESC dsbdesc; 
	LPDIRECTSOUNDBUFFER pDsb = NULL;
	LPDIRECTSOUNDBUFFER8 ppDsb8 = NULL;
	HRESULT hr; 
	 
	// Set up WAV format structure. 

	memset(&wfx, 0, sizeof(WAVEFORMATEX)); 
	wfx.wFormatTag = WAVE_FORMAT_PCM; 
	wfx.nChannels = 2; 
	wfx.nSamplesPerSec = 44100; 
	wfx.nBlockAlign = 4; 
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; 
	wfx.wBitsPerSample = 16; 
	 
	// Set up DSBUFFERDESC structure. 
	 
	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
	dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
	dsbdesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; 
	dsbdesc.dwBufferBytes = 2 * wfx.nAvgBytesPerSec; 
	dsbdesc.lpwfxFormat = &wfx; 
	 
	// Create buffer.	 
	
	hr = lpds->CreateSoundBuffer(&dsbdesc, &pDsb, NULL); 
	if (SUCCEEDED(hr)) 
	{ 
		hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID *)&ppDsb8);
		pDsb->Release();		
	}

	//tempStream = new StreamingSounddx();
	tempStream = new StreamingSounddx(fileName.c_str(), ppDsb8, dsbdesc.dwBufferBytes);
	return tempStream;
}





bool AudioDirectSound::init(){
	HRESULT hr;

    //Init COM
	hr = CoInitializeEx(NULL, 0);
    if (FAILED(hr))
    {
		coreGetLog()->WriteError("Can't init COM", __FILE__, __LINE__);
		return false;
    }

	
	//Create IDrectSound8
	hr = CoCreateInstance(CLSID_DirectSound8,
              NULL, 
              CLSCTX_INPROC_SERVER,
              IID_IDirectSound8,
              (LPVOID*) &lpds);
    if (FAILED(hr))
    {
      coreGetLog()->WriteError("Can't create IDrectSound8", __FILE__, __LINE__);
	  return false;
    }

	//Init IDrectSound8
	hr = lpds->Initialize(NULL);
    if (FAILED(hr))
    {
      coreGetLog()->WriteError("Can't init IDrectSound8", __FILE__, __LINE__);
	  return false;
    }
	
	hr = lpds->SetCooperativeLevel(coreGetMainWindow()->getHWnd(), DSSCL_PRIORITY);
	if (FAILED(hr))
	{
		coreGetLog()->WriteError("Can't set the CooperativeLevel for IDrectSound8", __FILE__, __LINE__);
		return false;
	}
    
	hr = getListener(lpds, &lpListener);
	if (FAILED(hr))
	{
		coreGetLog()->WriteError("Can't retrieve IDirectSound3DListener", __FILE__, __LINE__);
		return false;
	}

	//coreGetLog()->WriteError("Creation of DIRECTSOUND3DLISTENER failed. ", __FILE__, __LINE__);
	
	

    /*
	// Get listener parameters
    g_dsListenerParams.dwSize = sizeof(DS3DLISTENER);
    g_pDSListener->GetAllParameters( &g_dsListenerParams );	
	*/
	
	return true;
}



HRESULT AudioDirectSound::getListener(LPDIRECTSOUND8 _lpds, LPDIRECTSOUND3DLISTENER8* ppListener)
{
  DSBUFFERDESC             dsbd;
  LPDIRECTSOUNDBUFFER      lpdsbPrimary;  // Cannot be IDirectSoundBuffer8.
  HRESULT hr;
 
  ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
  dsbd.dwSize = sizeof(DSBUFFERDESC);
  dsbd.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;

  if (SUCCEEDED(hr = _lpds->CreateSoundBuffer(&dsbd, &lpdsbPrimary, NULL)))
  {
    hr = lpdsbPrimary->QueryInterface(IID_IDirectSound3DListener8,
                                 (LPVOID *)ppListener);
    lpdsbPrimary->Release();
  }
  return hr;
}

HRESULT CreateBasicBuffer(LPDIRECTSOUND8 lpDirectSound, LPDIRECTSOUNDBUFFER8* ppDsb8) 
{ 
  WAVEFORMATEX wfx; 
  DSBUFFERDESC dsbdesc; 
  LPDIRECTSOUNDBUFFER pDsb = NULL;
  HRESULT hr; 
 
  // Set up WAV format structure. 

  memset(&wfx, 0, sizeof(WAVEFORMATEX)); 
  wfx.wFormatTag = WAVE_FORMAT_PCM; 
  wfx.nChannels = 2; 
  wfx.nSamplesPerSec = 22050; 
  wfx.nBlockAlign = 4; 
  wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; 
  wfx.wBitsPerSample = 16; 
 
  // Set up DSBUFFERDESC structure. 
 
  memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
  dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
  dsbdesc.dwFlags = 
    DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY
    | DSBCAPS_GLOBALFOCUS; 
  dsbdesc.dwBufferBytes = 3 * wfx.nAvgBytesPerSec; 
  dsbdesc.lpwfxFormat = &wfx; 
 
  // Create buffer. 
 
  hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, &pDsb, NULL); 
  if (SUCCEEDED(hr)) 
  { 
     hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) ppDsb8);
     pDsb->Release();
  } 
  return hr;
}
	
void AudioDirectSound::destroy()
{
	if(lpListener){
		lpListener->Release();
		lpListener = NULL;
	}

	if(lpds){
		lpds->Release();
		lpds = NULL;
	}

	CoUninitialize();
}
	
void AudioDirectSound::update(){

}


}